
#ifndef Velyn_TriangleMesh_h
#define Velyn_TriangleMesh_h

#include <vector>

#include "OpenGL.h"
#include "Vector.h"
#include "Types.h"
// #include "Drawable.h"

namespace Velyn {

    class TriangleMesh {
        
    private:
        
        bool isDirty;
        
        VertexList vertexRecord;
        TriangleList triangleRecord;
        NormalList normalRecord;
        TextureList textureRecord;
        
    public:
        
        TriangleMesh() {}
        
        // ???: change the Drawables to take place here
        // instead of SubdivisionSurface
        
        // this needs to be tested
        // TriangleMesh(Drawable drawableType) : isDirty(true) {
            
        //    Drawable drawable(drawableType);
        //    drawable.draw(*this);
        
        // }
        
        TriangleMesh(const TriangleMesh& mesh) : isDirty(true) {
            this->vertexRecord = mesh.vertexRecord;
            this->normalRecord = mesh.normalRecord;
            this->textureRecord = mesh.textureRecord;
        }
        
        ~TriangleMesh() {}
        
        void clean() {
            isDirty = false;
        }
        
        bool dirty() {
            return isDirty;
        }
        
        void reset() {
            vertexRecord.clear();
            normalRecord.clear();
            textureRecord.clear();
            triangleRecord.clear();
        }
        
        // FIXME: change this to work with iterators
        void printVertices() {
            vector<Vertex>::iterator vertexIterator;
            int vertex = 0;
            
            cout << "Vertices:\n";
            for(vertexIterator = vertexRecord.begin(); vertexIterator != vertexRecord.end(); vertexIterator++) {
                cout << "v[" << vertex << "]";
                vertexIterator->print();
            }
            cout << '\n';
        }
        
        // FIXME: change this to work with iterators
        void printTriangles() {
            std::cout << "Triangles:\n";
            for(int triangle = 0; triangle < numberTriangles(); triangle++) {
                int triangleIndex = triangle * 3;
                
                cout << "t[" << triangle << "]";
                cout << "[ " << triangleRecord[triangleIndex] << " ";
                cout << " " << triangleRecord[triangleIndex + 1] << " ";
                cout << " " << triangleRecord[triangleIndex + 2] << " ]";
                cout << '\n';
            }
            cout << '\n';
        }
        
        // FIXME: change this to work with iterators
        void printNormals() {
            cout << "Normals:\n";
            for(int normal = 0; normal < normalRecord.size(); normal++) {
                cout << "n[" << normal << "]";
                normalRecord[normal].print();
            }
            cout << '\n';
        }
        
        void addVertex(const float x, const float y, const float z) {
            Vertex vertex;
            
            vertex.setValue(x, y, z);
            vertexRecord.push_back(vertex);
            
            isDirty = true;
        }
        
        VertexIndex addVertex(const Vertex vertex) {
            Vertex v;
            
            v.setValue(vertex[0], vertex[1], vertex[2]);
            vertexRecord.push_back(v);
            return (int)vertexRecord.size() - 1;
            
            isDirty = true;
        }
        
        void addTriangle(const VertexIndex index0, const VertexIndex index1, const VertexIndex index2) {
            triangleRecord.push_back(index0);
            triangleRecord.push_back(index1);
            triangleRecord.push_back(index2);
            
            isDirty = true;
        }
        
        void addNormal(const float x, const float y, const float z) {
            Normal normal;
            
            normal.setValue(x, y, z);
            normal.normalize();
            
            normalRecord.push_back(normal);
            
            isDirty = true;
        }
        
        void addNormal(const Normal normal) {
            normalRecord.push_back(normal);
            
            isDirty = true;
        }
        
        void addTexture(const float u, const float v) {
            Texture texture;
            
            texture.setValue(u, v);
            textureRecord.push_back(texture);
            
            isDirty = true;
        }
        
        void addTexture(Texture texture) {
            textureRecord.push_back(texture);
            
            isDirty = true;
        }
        
        VertexList getVertices(const TriangleIndex triangle) {
            VertexList vertex(3);
            int triangleIndex = triangle * 3;
            
            vertex[0] = vertexRecord[triangleRecord[triangleIndex]];
            vertex[1] = vertexRecord[triangleRecord[triangleIndex + 1]];
            vertex[2] = vertexRecord[triangleRecord[triangleIndex + 2]];
            
            return vertex;
        }
        
        TextureList getTextures(const TriangleIndex triangle) {
            TextureList textures(3);
            int triangleIndex = triangle * 3;
            
            textures[0] = textureRecord[triangleRecord[triangleIndex]];
            textures[1] = textureRecord[triangleRecord[triangleIndex + 1]];
            textures[2] = textureRecord[triangleRecord[triangleIndex + 2]];
            
            return textures;
        }
        
        VertexIndexList getVertexIndices(const TriangleIndex triangle) {
            VertexIndexList index(3);
            int triangleIndex = triangle * 3;
            
            index[0] = triangleRecord[triangleIndex];
            index[1] = triangleRecord[triangleIndex + 1];
            index[2] = triangleRecord[triangleIndex + 2];
            
            return index;
        }
        
        NormalList getNormals(const TriangleIndex triangle) {
            NormalList normals(3);
            int triangleIndex = triangle * 3;
            
            normals[0] = normalRecord[triangleRecord[triangleIndex]];
            normals[1] = normalRecord[triangleRecord[triangleIndex + 1]];
            normals[2] = normalRecord[triangleRecord[triangleIndex + 2]];
            
            return normals;
        }
        
        inline int numberTriangles() {
            return (int)(triangleRecord.size() / 3);
        }
        
        inline int numberVertices() {
            return (int)vertexRecord.size();
        }
        
        VertexList getVertexRecord() {
            return vertexRecord;
        }
        
        Vertex* getVertices() {
            return &(vertexRecord[0]);
        }

        TriangleIndex* getTriangles() {
            return &(triangleRecord[0]);
        }
        
        Normal* getNormals() {
            return &(normalRecord[0]);
        }    
        
        Texture* getTextures() {
            return &(textureRecord[0]);
        }

        VertexList* getVertexList() {
            return &vertexRecord;
        }
        
        TriangleList* getTriangleList() {
            return &triangleRecord;
        }
        
        NormalList* getNormalList() {
            return &normalRecord;
        }
        
        TextureList* getTextureList() {
            return &textureRecord;
        }
        
        // mutators
        void setVertex(int index, Vertex value) {
            vertexRecord[index] = value;
        }
        
        void setNormal(int index, Normal value) {
            normalRecord[index] = value;
        }
        
        // getters
        Vertex getVertex(int index) {
            return vertexRecord[index];
            
        }
        
        Normal getNormal(int index) {
            return normalRecord[index];
        }
        
        // FIXME: build iterators for normals and vertex
        // http://accu.org/index.php/journals/1527
        typedef vector<Vertex> :: iterator VertexIterator;
        
        VertexIterator getPositionBegin() {
            vector<Vertex> :: iterator vertexIterator = vertexRecord.begin();
            
            return vertexIterator;
        }
        
        VertexIterator getPositionEnd() {
            vector<Vertex> :: iterator vertexIterator = vertexRecord.end();
            
            return vertexIterator;
        }
        
        /* Vector3 centroid() {
            Vector3 result;
            
            vector<Vertex> :: iterator vertexIterator;
            
            for(vertexIterator = vertexRecord.begin(); vertexIterator != vertexRecord.end(); vertexIterator++) {
                result += (*vertexIterator); 
            }
            
            result /= vertexRecord.size();
            
            return result;
        } */
    };
}

#endif
